/**
 * Created by asus1 on 2015/10/26.
 */
// circles
// copyright Artan Sinani
// https://github.com/lugolabs/circles

/*
 Lightwheight JavaScript library that generates circular graphs in SVG.

 Call Circles.create(options) with the following options:

 id         - the DOM element that will hold the graph
 percentage - the percentage dictating the smaller circle
 radius     - the radius of the circles
 width      - the width of the ring (optional, has value 10, if not specified)
 number     - the number to display at the centre of the graph (optional, the percentage will show if not specified)
 text       - the text to display after the number (optional, nothing will show if not specified)
 colors     - an array of colors, with the first item coloring the full circle
 (optional, it will be `['#EEE', '#F00']` if not specified)
 duration   - value in ms of animation duration; (optional, defaults to 500);
 if `null` is passed, the animation will not run

 API:
 generate(radius) - regenerates the circle with the given radius (see spec/responsive.html for an example hot to create a responsive circle)

 */

(function() {
    var Circles = window.Circles = function(options) {
        var elId = options.id;
        this._el = document.getElementById(elId);

        if (this._el === null) return;


        this._radius         = options.radius;
        this._percentage     = options.percentage;
        this._text           = options.text; // #3
        this._number         = options.number || this._percentage;
        this._strokeWidth    = options.width  || 10;
        this._colors         = options.colors || ['#EEE', '#F00'];
        this._interval       = 16;
        this._textWrpClass   = 'circles-text-wrp';
        this._textClass      = 'circles-text';
        this._numberClass    = 'circles-number';

        this._confirmAnimation(options.duration);

        var endAngleRad      = Math.PI / 180 * 270;
        this._start          = -Math.PI / 180 * 90;
        this._startPrecise   = this._precise(this._start);
        this._circ           = endAngleRad - this._start;

        this.generate();

        if (this._canAnimate) this._animate();
    };

    Circles.prototype = {
        VERSION: '0.0.4',

        generate: function(radius) {
            if (radius) {
                this._radius = radius;
                this._canAnimate = false;
            }
            this._svgSize        = this._radius * 2;
            this._radiusAdjusted = this._radius - (this._strokeWidth / 2);
            this._el.innerHTML   = this._wrap(this._generateSvg() + this._generateText());
        },

        _confirmAnimation: function(duration) {
            if (duration === null) {
                this._canAnimate = false;
                return;
            }

            duration = duration || 500;

            var step     = duration / this._interval,
                pathFactor = this._percentage / step,
                numberFactor = this._number / step;

            if (this._percentage <= (1 + pathFactor)) {
                this._canAnimate = false;
            } else {
                this._canAnimate   = true;
                this._pathFactor   = pathFactor;
                this._numberFactor = numberFactor;
            }
        },

        _animate: function() {
            var i      = 1,
                self     = this,
                path     = this._el.getElementsByTagName('path')[1],
                numberEl = this._getNumberElement(),

                isInt    = this._number % 1 === 0,

                requestAnimFrame = window.requestAnimationFrame       ||
                    window.webkitRequestAnimationFrame ||
                    window.mozRequestAnimationFrame    ||
                    window.oRequestAnimationFrame      ||
                    window.msRequestAnimationFrame     ||
                    function (callback) {
                        setTimeout(callback, self._interval);
                    },

                animate = function() {
                    var percentage   = self._pathFactor * i,
                        nextPercentage = self._pathFactor * (i + 1),
                        number         = self._numberFactor * i,
                        canContinue    = true;
                    if (isInt) {
                        number = Math.round(number);
                    }
                    if (nextPercentage > self._percentage) {
                        percentage  = self._percentage;
                        number      = self._number;
                        canContinue = false;
                    }
                    if (percentage > self._percentage) return;
                    path.setAttribute('d', self._calculatePath(percentage, true));
                    numberEl.innerHTML = self._calculateNumber(number);
                    i++;
                    if (canContinue) requestAnimFrame(animate);
                };

            requestAnimFrame(animate);
        },

        _getNumberElement: function() {
            var divs = this._el.getElementsByTagName('span');
            for (var i = 0, l = divs.length; i < l; i++) {
                if (divs[i].className === this._numberClass) return divs[i];
            }
        },

        _wrap: function(content) {
            return '<div class="circles-wrp" style="position:relative; display:inline-block;">' + content + '</div>';
        },

        _generateText: function() {
            var html =  '<div class="' + this._textWrpClass + '" style="position:absolute; top:0; left:0; text-align:center; width:100%;' +
                ' font-size:' + this._radius * .7 + 'px; height:' + this._svgSize + 'px; line-height:' + this._svgSize + 'px;">' +
                this._calculateNumber(this._canAnimate ? 0 : this._number);
            if (this._text) {
                html += '<span class="' + this._textClass + '">' + this._text + '</span>';
            }
            html += '</div>';
            return html;
        },

        _calculateNumber: function(number) {
            var parts = (number + '').split('.'),
                html = '<span class="' + this._numberClass + '">' + parts[0];
            if (parts.length > 1) {
                html += '.<span style="font-size:.4em">' + parts[1].substring(0, 2) + '</span>';
            }
            return html + '</span>';
        },

        _generateSvg: function() {
            return '<svg width="' + this._svgSize + '" height="' + this._svgSize + '">' +
                this._generatePath(100, false, this._colors[0]) +
                this._generatePath(this._canAnimate ? 1 : this._percentage, true, this._colors[1]) +
                '</svg>';
        },

        _generatePath: function(percentage, open, color) {
            return '<path fill="transparent" stroke="' + color + '" stroke-width="' + this._strokeWidth + '" d="' + this._calculatePath(percentage, open) + '"/>';
        },

        _calculatePath: function(percentage, open) {
            var end      = this._start + ((percentage / 100) * this._circ),
                endPrecise = this._precise(end);
            return this._arc(endPrecise, open);
        },

        _arc: function(end, open) {
            var endAdjusted = end - 0.001,
                longArc       = end - this._startPrecise < Math.PI ? 0 : 1;

            return [
                'M',
                this._radius + this._radiusAdjusted * Math.cos(this._startPrecise),
                this._radius + this._radiusAdjusted * Math.sin(this._startPrecise),
                'A', // arcTo
                this._radiusAdjusted, // x radius
                this._radiusAdjusted, // y radius
                0, // slanting
                longArc, // long or short arc
                1, // clockwise
                this._radius + this._radiusAdjusted * Math.cos(endAdjusted),
                this._radius + this._radiusAdjusted * Math.sin(endAdjusted),
                open ? '' : 'Z' // close
            ].join(' ');
        },

        _precise: function(value) {
            return Math.round(value * 1000) / 1000;
        }
    };

    Circles.create = function(options) {
        return new Circles(options);
    };
})();